package com.springframework.boot.awesome.annotation.arch.util;

import com.springframework.boot.awesome.annotation.arch.exception.FTPCloseErrorException;
import com.springframework.boot.awesome.annotation.arch.exception.IPErrorException;
import com.springframework.boot.awesome.annotation.arch.exception.PortErrorException;
import com.springframework.boot.awesome.annotation.arch.exception.UserNameOrPasswordErrorException;
import com.springframework.boot.awesome.annotation.arch.entity.FTPConfig;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPReply;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeoutException;

public class FTPUtil {

    public static void monitorFile(long frequency, Method handleFileMethod, Object obj, FTPConfig ftpConfig, long timeout)
            throws TimeoutException, InterruptedException, UserNameOrPasswordErrorException, PortErrorException,
            IPErrorException, FTPCloseErrorException, IOException, InvocationTargetException, IllegalAccessException {
        FTPClient ftpClient = null;
        try{
            long startTime = System.currentTimeMillis();
            String host = ftpConfig.getIp();
            int port = Integer.valueOf(ftpConfig.getPort());
            String username = ftpConfig.getUsername();
            String password = ftpConfig.getPassword();
            ftpClient = getFTPClient(host, port, username, password);

            while (true){
                ftpClient.changeWorkingDirectory(ftpConfig.getPath());
                //如果Linux搭建的ftp环境没有开启被动模式传输数据时，一般不会读取到指定文件夹内的文件名称，需要主动开启
                ftpClient.enterLocalPassiveMode();
                String[] ftpFiles = ftpClient.listNames();
                List<String> fileNameList = new ArrayList();
                for(String fileName : ftpFiles){
                    fileNameList.add(fileName);
                }
                Object[] arguments = new Object[]{fileNameList};
                Object flag = handleFileMethod.invoke(obj, arguments);
                if(flag instanceof Boolean && (boolean)flag){
                    break;
                }
                if(System.currentTimeMillis() - startTime > timeout){
                    throw new TimeoutException("file is not exists, millisecondsTimeout: " + timeout);
                }else{
                    Thread.sleep(frequency);
                }
            }
        }finally {
            if(ftpClient != null){
                closeFTP(ftpClient);
            }
        }
    }


    private static FTPClient getFTPClient(String ftpHost, int ftpPort,
                                  String ftpUserName, String ftpPassword) throws UserNameOrPasswordErrorException, IPErrorException, PortErrorException {
        FTPClient ftp = null;
        try {
            ftp = new FTPClient();
            ftp.connect(ftpHost, ftpPort);
            ftp.login(ftpUserName, ftpPassword);
            ftp.setControlEncoding("UTF-8");
            if (!FTPReply.isPositiveCompletion(ftp.getReplyCode())) {
                ftp.disconnect();
                throw new UserNameOrPasswordErrorException("username or password error, username: "
                        + ftpUserName + ", password: " + ftpPassword);
            }

        } catch (SocketException e) {
            e.printStackTrace();
            throw new IPErrorException("FTP IP error: " + ftpHost);
        } catch (IOException e) {
            e.printStackTrace();
            throw new PortErrorException("FTP port error: " + ftpPort);
        } catch (UserNameOrPasswordErrorException e) {
            e.printStackTrace();
            throw e;
        }
        return ftp;
    }

    private static boolean closeFTP(FTPClient ftp) throws FTPCloseErrorException {
        try {
            ftp.logout();
        } catch (Exception e) {
            e.printStackTrace();
            throw new FTPCloseErrorException("FTP closed failed");
        }finally{
            if (ftp.isConnected()) {
                try {
                    ftp.disconnect();
                } catch (IOException e) {
                    e.printStackTrace();
                    throw new FTPCloseErrorException("FTP closed failed");
                }
            }
        }
        return false;
    }
}
